/***************************************************************************
 *   oboe.math.Number3D							*
 *   Copyright (C) 2011 by Felipe Manga					*
 *   lufeboma@gmail.com							*
 *									 *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the	   *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to	*
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:						 *
 *									 *
 *   The above copyright notice and this permission notice shall be	*
 *   included in all copies or substantial portions of the Software.	   *
 *									 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF	*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.					   *
 ***************************************************************************/

package oboe.math;

import flash.utils.ByteArray;

/**
 * A class for facilitating math with 3D vectors.. 
 * @author FelipeManga
 * 
 */
class Number3D
{
/**
 * The X (width) component 
 */
public var x : Float;

/**
 * The Y (height) component 
 */
public var y : Float;

/**
 * The Z (depth) component 
 */
public var z : Float;

/**
 * Class constructor for initializing x/y/z 
 * @param x
 * @param y
 * @param z
 * 
 */
public function new( x : Float, y : Float, z : Float )
{
	this.x = x;
	this.y = y;
	this.z = z;
}

/**
 * Clone function provided for compatibility with code written for Away3D. 
 * @param n The Number3D to read data from.
 * 
 */
public function clone( n : Float3D ) : Void
{
	this.x = n.x;
	this.y = n.y;
	this.z = n.z;
}

/**
 * Tests if this == n 
 * @param n the number to test equality with
 * @return true if this object's components have the same value as n's.
 * 
 */
public function isEqual( n : Float3D ) : Bool
{
	return this.x == n.x && this.y == n.y && this.z == n.z;
}

/**
 * Converts this object into a json-style string. 
 * @return the x/y/z components in a string. Example: "{ x:0, y:1, z:0 }"
 * 
 */
public function toString() : String 
{
	return "{ x: " + x + ", y: " + y + ", z: " + z + " }"; 
}


/**
 * Reads the 3 components as floats from a ByteArray. 
 * @param ba the byte array
 * @return this
 * 
 */
public function readFloat( ba : ByteArray ) : Float3D 
{
	x = ba.readFloat();
	y = ba.readFloat();
	z = ba.readFloat();
	return this;
}

/**
 * Reads the 3 components as unsigned bytes from a ByteArray. 
 * @param ba the byte array
 * @return this
 * 
 */
public function readByte( ba : ByteArray ) : Float3D 
{
	x = ba.readByte() & 0xFF;
	y = ba.readByte() & 0xFF;
	z = ba.readByte() & 0xFF;
	return this;
}	

public function mod ( o:Float ):Float3D
{
	if( o<0 ) o=-o;
	
	var t:Float = int(x/o);
	if( x<0 ) x += o;
	else x-=0;
	
	t = int(y/o);
	if( y<0 ) y += o;
	else y-=0;

	t = int(z/o);
	if( z<0 ) z += o;
	else z-=0;
	
	return this;
}

// direct port from prerenderer source code
/**
 * multiplies each of this object's components by a certain amount
 * @param o the amount to multiply by
 * @return this
 * 
 */
public function mul ( o : Float ) : Float3D 
{
	x *= o;
	y *= o;
	z *= o;
	return this;
}

/**
 * Multiplies this vector by another, overwriting it's own components. 
 * @param o The vector to multiply by.
 * @return this
 * 
 */
public function mul3 ( o : Float3D ) : Float3D 
{
	x *= o.x;
	y *= o.y;
	z *= o.z;
	return this;
}

/**
 * Multiplies this object's x/y/z components by the parameters and adds them together (does not modify x/y/z).  
 * @param a the value to multiply X by
 * @param b the value to multiply Y by
 * @param c the value to multiply Z by
 * @return the sum of the components multiplied by a, b, and c.
 * 
 */
public function mulsum2x ( a : Float, b : Float, c : Float ) : Float 
{
	return x*a + y*b + z*c;
}

/**
 * Multiplies this object's x/y/z components by a vector and adds them together (does not modify x/y/z).  
 * @param o the vector to multiply by
 * @return the sum of the components multiplied by the passed vector.
 * 
 */
public function mulsum3 ( o : Float3D ) : Float 
{
	return x*o.x + y*o.y + z*o.z;
}
	
public function div ( o : Float ) : Float3D 
{
	x /= o;
	y /= o;
	z /= o;
	return this;
}
public function div3 ( o : Float3D ) : Float3D {
	x /= o.x;
	y /= o.y;
	z /= o.z;
	return this;
}
	
public function add ( o : Float ) : Float3D {
	x += o;
	y += o;
	z += o;
	return this;
}
public function add3 ( o : Float3D ) : Float3D {
	x += o.x;
	y += o.y;
	z += o.z;
	return this;
}
		
public function sub3 ( o : Float3D ) : Float3D {
	x -= o.x;
	y -= o.y;
	z -= o.z;
	return this;
}
			
public function copy3( o : Float3D ) : Float3D {
	x = o.x;
	y = o.y;
	z = o.z;
	return this;
}

public function copy2( x : Float, y : Float, z : Float ) : Float3D {
	this.x = x;
	this.y = y;
	this.z = z;
	return this;
}

public function copy( o : Float ) : Float3D {
	x = o;
	y = o;
	z = o;
	return this;
}

public function flipYZ() : Float3D {
	var t : Float = y;
	y = z;
	z = t;
	return this;
}

public function flipXY() : Float3D {
	var t : Float = y;
	y = x;
	x = t;
	return this;
}

public function flipXZ() : Float3D {
	var t : Float = x;
	x = z;
	z = t;
	return this;
}

public function cross( v1 : Float3D, v2 : Float3D ) : Float3D 
{
	x=v1.y*v2.z-v1.z*v2.y;
	y=v1.z*v2.x-v1.x*v2.z;
	z=v1.x*v2.y-v1.y*v2.x;
	return this;
}

public function lerp( v1 : Float3D, v2 : Float3D, f:Float ): Float3D
{
	x = v1.x*(1-f) + v2.x*f;
	y = v1.y*(1-f) + v2.y*f;
	z = v1.z*(1-f) + v2.z*f;
	return this;
}

public function dotProduct( v2 : Float3D ) : Float
{
	// return mulsum3( v2 );
	return x*v2.x + y*v2.y + z*v2.z;
}

public function length() : Float {
	return Math.sqrt( x*x+y*y+z*z );
}

public function lengthSquared() : Float {
	return x*x+y*y+z*z;
}

public function normalize() : Float3D 
{
	var d : Float = this.length();
	if( d>0 ){
	x/=d;
	y/=d;
	z/=d;
	}else{
	x=y=z=0;
	}
	return this;
}

public function floor():Float3D
{
	x=int(x);
	y=int(y);
	z=int(z);
	return this;
}

public function setRandom( min : Float, max : Float ) : Float3D 
{
	x = min + Math.random()*(max-min);
	y = min + Math.random()*(max-min);
	z = min + Math.random()*(max-min);
	return this;
}
}